package fybug.nulll.pdstream.IO.Stream;
import com.sun.istack.internal.NotNull;
import com.sun.istack.internal.Nullable;

import java.io.InputStream;
import java.io.OutputStream;

import fybug.nulll.pdfunctionlibrary.Processing.Check;
import fybug.nulll.pdfunctionlibrary.Processing.Err.Stop;
import fybug.nulll.pdfunctionlibrary.Util.Processing.Arrarys;
import fybug.nulll.pdstream.IO.Error.ByteStreamErr;
import fybug.nulll.pdstream.IO.I_O;
import fybug.nulll.pdstream.Interface.UpdateSize;
import fybug.nulll.pdstream.Source.StreamSource;
import fybug.nulll.pdstream.StreamOperator;
/**
 * <h2>字节流复制器.</h2>
 * <pre>
 * <b>建议使用工场类 {@link I_O} 构造</b>
 *
 * 从绑定的 <b>操作源</b> 中获取的 <b>输出流</b> 和绑定 <b>被复制流</b> 进行复制
 * 支持 <b>多个被复制流</b> 复制到 <b>多个输出流</b>
 * 可传入 <b>不绑定在源中</b> 的要复制的被复制流
 * 可使用该操作器中的方法对操作源进行 <b>输出流和被复制流的重绑定</b>
 * 可以使用 <b>连续操作</b> ,有效解决不稳定流的处理问题</pre>
 * <pre>常规操作方法：
 * {@link #copy()}
 * {@link #copy(InputStream...)}
 * </pre>
 *
 * @author fybug
 * @version 0.0.6
 * @see StreamOperator
 * @see InputStream
 * @see OutputStream
 * @see I_O
 * @see ByteStreamErr
 * @since JDK 1.8
 */
@SuppressWarnings( "all" )
public final
class CopyByte extends StreamOperator<OutputStream, InputStream[]> {
    /** <p>构造一个操作器并绑定操作源.</p> */
    public
    CopyByte(@Nullable final StreamSource<OutputStream, InputStream[]> r) { binSource(r); }

    /*
     * MainSource
     */

    /** <p>绑定字节源.</p> */
    @NotNull
    public final
    CopyByte binSource(@Nullable final StreamSource<OutputStream, InputStream[]> r)
    { return (CopyByte) super.binSource(r); }

    /** <p>重绑定操作源中的流.</p> */
    @NotNull
    public final
    CopyByte binCopyTo(@Nullable final OutputStream... r) {
        try {
            source().binStream(r);
        } catch ( Stop ignored ) {
        }
        return this;
    }

    /** <p>增加操作源中的流.</p> */
    @NotNull
    public final
    CopyByte appendCopyTo(final OutputStream... r) {
        @Nullable OutputStream[] outputStreams;
        try {
            @NotNull final StreamSource<OutputStream, InputStream[]> source = source();
            try {
                outputStreams = source.stream();
            } catch ( Stop e ) {
                outputStreams = null;
            }
            source.binStream(Arrarys.append(outputStreams, r));
        } catch ( Stop ignored ) {
        }
        return this;
    }

    /** <p>重绑定操作源中的被复制流.</p> */
    @NotNull
    public final
    CopyByte binCopyFrom(@Nullable final InputStream... r) {
        try {
            source().binOutPut(r);
        } catch ( Stop ignored ) {
        }
        return this;
    }

    /** <p>增加操作源中的被复制流.</p> */
    @NotNull
    public final
    CopyByte appendCopyFrom(final InputStream... r) {
        try {
            @NotNull final StreamSource<OutputStream, InputStream[]> source = source();
            InputStream[] inputStreams;
            try {
                inputStreams = source.getOutPut();
            } catch ( Stop e ) {
                inputStreams = null;
            }
            source.binOutPut(Arrarys.append(inputStreams, r));
        } catch ( Stop ignored ) {
        }
        return this;
    }

    /*
     * Copy
     */

    /**
     * <p>将绑定的被复制流中的数据输出到绑定的输出流中.</p>
     * <pre>
     * 从源中绑定的所有被复制流获取数据并输出到所有输出流中
     * 适用于所有流
     *
     * 但不建议使用该方法 <b>复制大量数据</b>
     * 会 <b>内存不足</b> 的
     * </pre>
     *
     * @throws ByteStreamErr 复制过程出错
     */
    @NotNull
    public final
    CopyByte copy() throws ByteStreamErr {
        try {
            copy(source().getOutPut());
        } catch ( Stop ignored ) {
        }
        return this;
    }

    /**
     * <p>将输入的被复制流中的数据输出到绑定的输出流中.</p>
     * <pre>
     * 从输入的所有被复制流获取数据并输出到绑定的所有输出流中
     * 适用于所有流
     *
     * 但不建议使用该方法 <b>复制大量数据</b>
     * 会 <b>内存不足</b> 的
     * </pre>
     *
     * @throws ByteStreamErr 复制过程出错
     */
    @NotNull
    public final
    CopyByte copy(@Nullable InputStream... r) throws ByteStreamErr {
        @NotNull OutputStream[] w;
        try {
            w = source().stream();
            r = Check.checkNull(Arrarys.checkNullArray(Arrarys.trim(r)));
        } catch ( Stop e ) {
            return this;
        }
        @NotNull byte[] bytesBuffer = null; // 读取暂存区
        @NotNull InputStream reader = null;
        int readSize;
        try {
            synchronized ( w ){
                for ( int i = 0, lenght; i < r.length; i++ ){
                    /* 读取所有流 */
                    reader = r[i];
                    // 检查长度
                    lenght = reader.available();
                    if (lenght == 0)
                        continue;
                    // 准备缓冲区
                    if (bytesBuffer == null)
                        bytesBuffer = new byte[lenght];
                    else if (bytesBuffer.length != lenght)
                        bytesBuffer = new byte[lenght];
                    readSize = reader.read(bytesBuffer);
                    if (readSize > 0)
                        // 有读取
                        forWrite(w, bytesBuffer, readSize);
                }
            }
            bytesBuffer = null;
            readSize = 0;
            reader = null;
        } catch ( java.io.IOException | ByteStreamErr e ) {
            throw new ByteStreamErr(e.getMessage(), reader,
                                    (e instanceof ByteStreamErr) ? ((ByteStreamErr) e).outstream
                                            : null);
        }
        flush(w);
        return this;
    }

    /**
     * <p>使用连续操作将绑定的被复制流中的数据复制到绑定的输出流中.</p>
     * <pre>
     * 按顺序从被复制流中读取数据，并放入输出流中
     * 每次复制一个片段
     * 每个片段都会写入所有输出流中
     * 内存消耗取决于片段大小
     * 适用于所有流
     * </pre>
     *
     * @param size 片段大小
     *
     * @throws ByteStreamErr 复制出错
     */
    @NotNull
    public final
    CopyByte sustained(final int size) throws ByteStreamErr
    { return monitor(size, checkUpSize()); }

    /**
     * <p>使用连续操作将绑定的被复制流中的数据复制到绑定的输出流中.</p>
     * <pre>
     * 按顺序从被复制流中读取数据，并放入输出流中
     * 每次复制一个片段
     * 可以使用接口 <b>动态调整片段大小</b>
     * 每个片段都会写入所有输出流中
     * 内存消耗取决于片段大小
     * 将片段大小调整为{@code 0} 可中断复制
     * 适用与所有流
     * </pre>
     *
     * @param size 初始片段大小
     * @param see  监听接口
     *
     * @throws ByteStreamErr 复制出错
     * @see UpdateSize
     */
    @NotNull
    public final
    CopyByte monitor(final int size, @Nullable final UpdateSize see) throws ByteStreamErr
    {
        @NotNull OutputStream[] w;
        @NotNull InputStream[] r;
        try {
            w = source().stream();
            r = source().getOutPut();
            if (size <= 0)
                return this;
        } catch ( Stop e ) {
            return this;
        }
        @NotNull InputStream reader = null;
        int readSize; // 当前读取数量
        try {
            synchronized ( w ){
                if (see != null)
                    see.init();
                @NotNull byte[] bytesBuffer = new byte[size];
                for ( int i = 0; i < r.length; i++ ){
                    reader = r[i]; // 减少寻址
                    while( (readSize = reader.read(bytesBuffer)) > -1 ){
                        if (readSize > 0) {
                            forWrite(w, bytesBuffer, readSize);
                            if (readSize < bytesBuffer.length)
                                /* 读取完成 */
                                break;
                        }
                        bytesBuffer = Buff.upBufferSize(see, bytesBuffer, readSize);
                    }
                }
            }
        } catch ( Stop ignored ) {
        } catch ( java.io.IOException | ByteStreamErr e ) {
            throw new ByteStreamErr(e.getMessage(), reader,
                                    (e instanceof ByteStreamErr) ? ((ByteStreamErr) e).outstream
                                            : null);
        } finally {
            if (see != null)
                see.destroy();
        }
        flush(w);
        return this;
    }

    /*
     * 流操作
     */

    // 集体刷新
    private
    void flush(@NotNull final OutputStream... w) throws ByteStreamErr {
        @Nullable OutputStream write = null;
        try {
            synchronized ( w ){
                for ( int i = 0; i < w.length; i++ ){
                    /* 刷新流 */
                    write = w[i];
                    write.flush();
                }
            }
        } catch ( java.io.IOException e ) {
            throw new ByteStreamErr(e.getMessage(), null, write);
        }
    }

    private
    void forWrite(@NotNull final OutputStream[] w, @NotNull final byte[] bytescache, int lenght)
    throws ByteStreamErr
    {
        @NotNull OutputStream writer = null;
        try {
            for ( int i = 0; i < w.length; i++ ){
                /* 写入所有 */
                writer = w[i]; // 减少寻址
                writer.write(bytescache, 0, lenght);
            }
        } catch ( java.io.IOException e ) {
            throw new ByteStreamErr(e.getMessage(), null, writer);
        }
    }
}